/* This file is part of the sirit project.
 * Copyright (c) 2019 sirit
 * This software may be used and distributed according to the terms of the
 * 3-Clause BSD License
 */

#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <sirit/sirit.h>

class MyModule : public Sirit::Module {
public:
    MyModule() : Sirit::Module{0x00010300} {}
    ~MyModule() = default;

    void Generate() {
        AddCapability(spv::Capability::Shader);
        SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);

        const auto t_void = Name(TypeVoid(), "void");
        const auto t_uint = Name(TypeInt(32, false), "uint");
        const auto t_float = Name(TypeFloat(32), "float");

        const auto float4 = Name(TypeVector(t_float, 4), "float4");
        const auto in_float = Name(TypePointer(spv::StorageClass::Input, t_float), "in_float");
        const auto in_float4 = Name(TypePointer(spv::StorageClass::Input, float4), "in_float4");
        const auto out_float4 = Name(TypePointer(spv::StorageClass::Output, float4), "out_float4");

        const auto gl_per_vertex = Name(TypeStruct(float4), "gl_PerVertex");
        const auto gl_per_vertex_ptr =
            Name(TypePointer(spv::StorageClass::Output, gl_per_vertex), "out_gl_PerVertex");

        const auto in_pos = Name(AddGlobalVariable(in_float4, spv::StorageClass::Input), "in_pos");
        const auto per_vertex =
            Name(AddGlobalVariable(gl_per_vertex_ptr, spv::StorageClass::Output), "per_vertex");

        Decorate(in_pos, spv::Decoration::Location, 0);
        Decorate(gl_per_vertex, spv::Decoration::Block);
        Decorate(gl_per_vertex, spv::Decoration::Block);
        MemberDecorate(gl_per_vertex, 0, spv::Decoration::BuiltIn,
                       static_cast<std::uint32_t>(spv::BuiltIn::Position));

        const auto main_func = Name(
            OpFunction(t_void, spv::FunctionControlMask::MaskNone, TypeFunction(t_void)), "main");
        AddLabel();

        const auto ptr_pos_x = OpAccessChain(in_float, in_pos, Constant(t_uint, 0u));
        const auto ptr_pos_y = OpAccessChain(in_float, in_pos, Constant(t_uint, 1u));

        const auto pos_x = OpLoad(t_float, ptr_pos_x);
        const auto pos_y = OpLoad(t_float, ptr_pos_y);

        auto tmp_position = OpUndef(float4);
        tmp_position = OpCompositeInsert(float4, pos_x, tmp_position, 0);
        tmp_position = OpCompositeInsert(float4, pos_y, tmp_position, 1);
        tmp_position = OpCompositeInsert(float4, Constant(t_float, 0.0f), tmp_position, 2);
        tmp_position = OpCompositeInsert(float4, Constant(t_float, 1.0f), tmp_position, 3);

        const auto gl_position = OpAccessChain(out_float4, per_vertex, Constant(t_uint, 0u));
        OpStore(gl_position, tmp_position);

        OpReturn();
        OpFunctionEnd();

        AddEntryPoint(spv::ExecutionModel::Vertex, main_func, "main", in_pos, per_vertex);
    }
};

static constexpr std::uint8_t expected_binary[] = {
    0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0d, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
    0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x76, 0x6f, 0x69, 0x64,
    0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x69, 0x6e, 0x74,
    0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x6f, 0x61,
    0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0x6c, 0x6f, 0x61,
    0x74, 0x34, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x5f, 0x66,
    0x6c, 0x6f, 0x61, 0x74, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
    0x69, 0x6e, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00,
    0x07, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x00, 0x00,
    0x05, 0x00, 0x06, 0x00, 0x08, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65,
    0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00,
    0x6f, 0x75, 0x74, 0x5f, 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78,
    0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x5f, 0x70,
    0x6f, 0x73, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x70, 0x65, 0x72, 0x5f,
    0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00,
    0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00,
    0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
    0x48, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00,
    0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00,
    0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
    0x08, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x2b, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2b, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x2b, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
    0x3b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x3b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
    0x36, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x0c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
    0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
    0x41, 0x00, 0x05, 0x00, 0x05, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
    0x11, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
    0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
    0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
    0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
    0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x17, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
    0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x52, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00,
    0x1b, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
    0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
    0x0f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00,
    0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};

int main(int argc, char** argv) {
    MyModule module;
    module.Generate();

    std::vector<std::uint32_t> code = module.Assemble();
    if (std::size(code) * sizeof(std::uint32_t) != std::size(expected_binary)) {
        return EXIT_FAILURE;
    }
    if (std::memcmp(std::data(code), std::data(expected_binary), std::size(expected_binary)) != 0) {
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
